package com.aaa.Oauth2;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import javax.annotation.Resource;
import javax.sql.DataSource;

@Configuration
// 启用认证服务器
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
    @Resource
    public PasswordEncoder passwordEncoder;
    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    DataSource dataSource;

    /*** 令牌转换器：配置jwt生成和解析的需要的秘钥 * <p> * 生成jwt需要秘钥所有需要注入 JwtAccessTokenConverter ** @return */
    @Bean
    protected JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        jwtAccessTokenConverter.setSigningKey("dev");
        return jwtAccessTokenConverter;
    }

    /*** 在spring容器中放置JwtTokenStore ** @return */
    @Bean
    protected TokenStore jwtTokenStore() {
// return new InMemoryTokenStore(); // 默认存储方式
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    /*** 配置认证过程：security认证 ** @param endpoints * @throws Exception */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
//        设置参数信息
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
//        允许刷新token
        defaultTokenServices.setSupportRefreshToken(true);
//        设置存储方式
        defaultTokenServices.setTokenStore(jwtTokenStore());
        endpoints.accessTokenConverter(jwtAccessTokenConverter())
                //token存储方式
                .tokenStore(jwtTokenStore())
                .authenticationManager(authenticationManager);
    }

    /*** ClientDetailsServiceConfigurer：
     在认证中心备案客户端，支持谁来获取token
     用来配置客户端详情服务（ClientDetailsService），
     客户端详情信息在这里进行初始化，
     能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息
     authorization_code：授权码类型
     1.授权码模式（authorization_code）
     * 2.简化模式（implicit） *
     * 3.密码模式（resource owner password credentials） *
     * 4.客户端模式（client_credentials） *
     * 5.通过以上授权获得的刷新令牌来获取新的令牌（refresh_token） ** @param clients * @throws Exception */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        //使用数据库进行客户端配置
        clients.jdbc(dataSource);
        // 保存在内存中
     /* clients.inMemory().withClient("customer")
                // 密钥
                .secret(passwordEncoder.encode("123"))
                // 授权类型
                .authorizedGrantTypes("refresh_token", "authorization_code", "password", "client_credentials")
                // token的有效期
                .accessTokenValiditySeconds(3600)
                // 用来限制客户端访问的权限，在换取的token的时候会带上scope参数，
                // 只有在 scopes定义内的，才可以正常换取token
                .scopes("all", "read", "write", "a").and().withClient("product").secret(passwordEncoder.encode("123"))

                .authorizedGrantTypes("refresh_token", "authorization_code", "password").accessTokenValiditySeconds(3600).scopes("all");*/
    }

    /*** AuthorizationServerSecurityConfigurer 用来配置令牌端点(Token Endpoint)的安全 约束 ** @param security * @throws Exception */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        // 允许客户端访问 OAuth2 授权接口，否则请求token会返回 401
        security.allowFormAuthenticationForClients();
        /*** 允许已授权用户访问 checkToken 接口和获取 token 接口 * POST /oauth/authorize 授权码模式认证授权接口 * GET/POST /oauth/token 获取 token 的接口 * POST /oauth/check_token 检查 token合法性接口 */
        security.checkTokenAccess("permitAll()");
        security.tokenKeyAccess("permitAll()");
    }
}